const { Filter } = require("../../display/filters")
const { translateFilters } = require("../../display/filters")
const { Display, translateValue, translateBloon, damageModValue, translateCorvusSpells } = require("../../display/general")
const { ignoreFunction } = require("../../util/ignore")
const { getModelType, diagonalLength } = require("../../util/utils")
const { parseAbility } = require("../ability/parse")
const { parseAttack } = require("../attack/parse")
const { parseEmission } = require("../emission/emission")
const { parseFilters } = require("../filter/parse")
const { parseProjectileBehavior } = require("../projectile/behavior")
const { parseProjectile } = require("../projectile/parse")

const models = {}

models.AbilityModel = (b, t) => {
    t.abilities.push(parseAbility(b))
}

models.ActivateAbilityAfterIntervalModel = ignoreFunction

models.AirUnitModel = (b, t) => {
    b.behaviors.forEach(e => parseTowerBehavior(e, t))
}

models.AttackModel = (b, t) => {
    t.attacks.push(...parseAttack(b))
}
models.AttackAirUnitModel = models.AttackModel

models.BankModel = (b, t) => {
    t.meta.bankCapacity = b.capacity
    t.meta.interestPerRound = Display.percent(b.interest)
}

models.BeastHandlerLeashModel = (b, t) => {
    const { parseTower } = require("./parse")
    if (!b.towerModel) return
    t.subTowers.push(
        parseTower(b.towerModel),
        b.towerModelSecond ? parseTower(b.towerModelSecond) : null
    )
}

models.BonusLivesPerRoundModel = (b, t) => {
    t.meta.livesPerRound = b.amount
}

models.CashIncreaseModel = (b, t) => {
    t.meta.bloonCashMultiplier = Display.additivePercent(b.multiplier - 1)
}

models.ComancheDefenceModel = (b, t) => {
    const { parseTower } = require("./parse")
    const subTower = parseTower(b.towerModel)
    subTower.meta.towerCount = b.reinforcementCount
    subTower.meta.lifespan = b.durationFrames < 9999999 ? Display.time(b.durationFrames / 60) : null
    t.subTowers.push(subTower)
}

models.CreateNearbyWaterModel = (b, t) => {
    t.zones.push({
        name: translateValue('createWaterZone', 'create water nearby'),
        radius: b.radius
    })
}

models.CreateProjectileOnTowerDestroyModel = (b, t) => {
    const proj = parseProjectile(b.projectileModel)
    parseEmission(b.emissionModel, proj, null)
    t.meta.projectileOnDestroy = proj
}

models.DroneSupportModel = (b, t) => {
    const { parseTower } = require("./parse")
    const tower = parseTower(b.droneModel)
    tower.meta.towerCount = b.count
    t.subTowers.push(tower)
}

models.LifeRegenModel = (b, t) => {
    t.meta.livesPerRound = b.regenAmount
    if (b.overRegenAmount > 0) {
        t.meta.maxLivesOverStartingLife = Display.additive(b.overRegenAmount)
    }
}

models.LinkProjectileRadiusToTowerRangeModel = ignoreFunction

models.MonkeyCityModel = (b, t) => {
    t.notes.push(translateValue('monkeyCityBonusTower', 'one dart monkey per round'))
}

models.MonkeyopolisModel = ignoreFunction

models.MultiHookManagerModel = (b, t) => {
    t.meta.delayBetweenHooks = Display.time(b.minTimeBetweenHooks)
    t.meta.delayBetweenAllHooks = Display.time(b.reloadTime)
}

models.ParagonSacrificeBonusModel = (b, t) => {
    t.meta.paragonPowerBonus = b.bonus
}

models.PerRoundCashBonusTowerModel = (b, t) => {
    t.meta.cashPerRound = b.cashPerRound
}

models.PreEmptiveStrikeLauncherModel = (b, t) => {
    t.meta.preEmptiveProjectile = parseProjectile(b.projectileModel)
}

models.RemoveMutatorOnUpgradeModel = ignoreFunction

models.ScaleHeroXpWithTowerCountModel = (b, t) => {
    // TODO: parse this thing
}

models.SilasIceFragmentsModel = (b, t) => {
    t.meta.iceFragProjectile = parseProjectile(b.iceFragmentProjectileModel)
}

models.SkimBloonsModel = (b, t) => {
    t.meta.cashPerBloonEntered = b.skimAmount
}

models.SwitchTargetSupplierOnUpgradeModel = ignoreFunction

models.TowerCreateParagonTowerModel = (b, t) => {
    const { parseTower } = require("./parse")
    t.subTowers.push(parseTower(b.towerModels[0]))
}

models.TowerCreateTowerModel = (b, t) => {
    const { parseTower } = require("./parse")
    if (!b.towerModel) return
    t.subTowers.push(parseTower(b.towerModel))
}

models.TowerExpireModel = (b, t) => {
    if (b.lifespan >= 9999999) return
    t.meta.rounds = b.rounds
    t.meta.lifespan = Display.time(b.lifespan)
}

models.TowerExpireOnParentDestroyedModel = (b, t) => {
    t.notes.push(translateValue('expireWhenParentTowerDestroyed', 'destroy when parent tower destroyed'))
}

models.TranceTotemSpawnerModel = (b, t) => {
    const { parseTower } = require("./parse")
    t.subTowers.push(parseTower(b.tower))
}

//----------------------------------------------------
// air unit & movement
//----------------------------------------------------

models.HeliMovementModel = (b, t) => {
    t.meta.speed = b.maxSpeed
}

models.PathMovementModel = (b, t) => {
    if (b.speed) {
        t.meta.speed = b.speed
    }
}

models.FighterMovementModel = (b, t) => {
    t.meta.speed = b.maxSpeed
}

models.PathMovementFromScreenCenterModel = ignoreFunction

models.RosaliaMovementModel = (b, t) => {
    t.meta.speed = b.maxSpeed
    t.meta.airUnitFootprint = b.jumpBlockerRadius
}

//----------------------------------------------------
// zone & buff
//----------------------------------------------------

models.AbilityCooldownScaleSupportModel = (b, t) => {
    t.buffs.push({
        name: translateValue('abilityCooldownZone', 'ability cooldown buff'),
        cooldownScaleReduction: b.abilityCooldownSpeedScale === 1 ? null : Display.additivePercent(1 - b.abilityCooldownSpeedScale),
        filters: translateFilters(parseFilters(b.filters)),
        radius: b.isCustomRadius && b.customRadius < diagonalLength ? b.customRadius : null,
        maxStacks: b.maxStackSize > 0 ? b.maxStackSize : null,
        notes: [
            b.affectSelf ? translateValue('affectsSelf', 'affects self') : null,
            b.isGlobalRange || b.customRadius >= diagonalLength ? translateValue('affectGlobal', 'has global range') : null,
            b.onlyAffectParagon ? translateValue('affectsOnlyParagon', 'only affects paragon') : null,
        ]
    })
}

models.AddBehaviorToBloonInZoneModel = (b, t) => {
    t.zones.push({
        name: translateValue('bloonBehaviorZone', 'bloon behavior zone'),
        mutationId: Display.mutationId(b.mutationId),
        filters: translateFilters(parseFilters(b.filters)),
    })
}

models.AddBehaviorToTowerSupportModel = (b, t) => {
    if (b.mutationId) {
        t.zones.push({
            name: translateValue('towerBehaviorZone', 'tower behavior zone'),
            mutationId: Display.mutationId(b.mutationId),
            filters: translateFilters(parseFilters(b.filters)),
        })
    }
}

models.BananaCashIncreaseSupportModel = (b, t) => {
    t.buffs.push({
        name: translateValue('incomeBuffZone', 'income generation buff'),
        mutationId: Display.mutationId(b.mutatorId),
        incomeMultiplier: Display.additivePercent(b.multiplier),
        minTier: b.useTiers ? `${b.firstPathTier < 0 ? 'x' : b.firstPathTier}${b.secondPathTier < 0 ? 'x' : b.secondPathTier}${b.thirdPathTier < 0 ? 'x' : b.thirdPathTier}` : null,
        filters: translateFilters(parseFilters(b.filters)),
        radius: b.isCustomRadius && b.customRadius < diagonalLength ? b.customRadius : null,
        maxStacks: b.maxStackSize > 0 ? b.maxStackSize : null,
        notes: [
            b.appliesToOwningTower ? translateValue('affectsSelf', 'affects self') : null,
            b.isGlobal || b.customRadius >= diagonalLength ? translateValue('affectGlobal', 'has global range') : null,
            b.onlyAffectParagon ? translateValue('affectsOnlyParagon', 'only affects paragon') : null,
        ]
    })
}

models.BananaCentralBuffModel = (b, t) => {
    t.buffs.push({
        name: translateValue('bananaCentralBuff', 'banana central buff'),
        mutationId: Display.mutationId(b.mutatorId),
        cashMultiplier: Display.multiplierPercent(b.multiplier)
    })
}

models.CashbackZoneModel = (b, t) => {
    t.buffs.push({
        name: translateValue('sellBackZone', 'sell back'),
        sellbackScaleBonus: Display.additivePercent(b.cashbackZoneMultiplier),
        maxStacks: b.maxStacks
    })
}

models.CentralMarketBuffModel = (b, t) => {
    t.buffs.push({
        name: translateValue('centralMarketBuff', 'central matket buff'),
        mutationId: Display.mutationId(b.mutatorId),
        cashMultiplier: Display.additivePercent(b.multiplier - 1),
        maxStacks: b.maxStackSize
    })
}

models.CollectCashZoneModel = ignoreFunction

models.DamageBasedAttackSpeedModel = (b, t) => {
    t.buffs.push({
        name: translateValue('attackSpeedBuffWhenDamaging', 'attack speed increase when dealing damage'),
        damagePerStack: b.damageThreshold,
        attackSpeedIncreasePerStack: Display.additivePercent(b.increasePerThreshold),
        maxStacks: b.maxStacks,
        lifespan: Display.time(b.maxTimeInFramesWithoutDamage / 60)
    })
}

models.DamageModifierSupportModel = (b, t) => {
    const fakeProj = { damageMods: [] }
    parseProjectileBehavior(b.damageModifierModel, fakeProj)
    t.buffs.push({
        name: translateValue('damageModsBuffZone', 'damage mods buff'),
        mutationId: Display.mutationId(b.mutatorId),
        damageMods: fakeProj.damageMods,
        filters: translateFilters(parseFilters(b.filters)),
        radius: b.isCustomRadius && b.customRadius < diagonalLength ? b.customRadius : null,
        maxStacks: b.maxStackSize > 0 ? b.maxStackSize : null,
        notes: [
            b.appliesToOwningTower ? translateValue('affectsSelf', 'affects self') : null,
            b.isGlobal || b.customRadius >= diagonalLength ? translateValue('affectGlobal', 'has global range') : null,
            b.onlyAffectParagon ? translateValue('affectsOnlyParagon', 'only affects paragon') : null,
        ]
    })
}

models.DamageOverTimeZoneModel = (b, t) => {
    let damageMods = []
    if (b.behaviorModel.damageModifierModels && b.behaviorModel.damageModifierModels.length !== 0) {
        const fakeProj = { damageMods: [] }
        b.behaviorModel.damageModifierModels.forEach(e => parseProjectileBehavior(e, fakeProj))
        damageMods = fakeProj.damageMods
    } else {
        b.behaviorModel.bloonTagsList.forEach(e => {
            damageMods.push({
                keyword: translateValue('damageModsKeywordTag', e, Display.tag(e)),
                value: damageModValue(b.behaviorModel.additive, b.behaviorModel.multiplier)
            })
        })
    }
    t.zones.push({
        name: translateValue('damageOverTimeZone', 'damage over time zone'),
        damage: b.behaviorModel.damage,
        damageMods: damageMods,
        damageType: b.behaviorModel.immuneBloonProperties,
        interval: Display.time(b.behaviorModel.interval),
    })
}

models.DamageTypeSupportModel = (b, t) => {
    t.buffs.push({
        name: translateValue('damageTypeBuffZone', 'damage type support'),
        mutationId: Display.mutationId(b.mutatorId),
        damageType: b.immuneBloonProperties,
        maxTier: b.tierCap,
        //towers: b.towerBaseIds ? b.towerBaseIdList.map(translateTower) : null,
        //upgrade: b.upgradeId ? b.upgradeId : null,
        maxStacks: b.maxStackSize > 0 ? b.maxStackSize : null,
        notes: [
            b.affectSelf ? translateValue('affectsSelf', 'affects self') : null,
            b.isGlobalRange || b.customRadius >= diagonalLength ? translateValue('affectGlobal', 'has global range') : null,
            b.onlyAffectParagon ? translateValue('affectsOnlyParagon', 'only affects paragon') : null,
        ]
    })
}

models.DiscountZoneModel = (b, t) => {
    t.buffs.push({
        name: translateValue('discountZone', 'discount'),
        discount: Display.additivePercent(-b.discountMultiplier),
        maxTier: b.tierCap,
        towers: b.towerBaseIds ? b.towerBaseIdList : null,
        upgrade: b.upgradeId ? b.upgradeId : null,
        maxStacks: b.maxStackSize > 0 ? b.maxStackSize : null,
        notes: [
            b.affectSelf ? translateValue('affectsSelf', 'affects self') : null,
            b.isGlobalRange || b.customRadius >= diagonalLength ? translateValue('affectGlobal', 'has global range') : null,
            b.onlyAffectParagon ? translateValue('affectsOnlyParagon', 'only affects paragon') : null,
        ]
    })
}

models.DruidOfWrathBuffModel = (b, t) => {
    t.buffs.push({
        name: translateValue('druidOfWrathBuff', 'druid of wrath buff'),
        damage: b.newPopCount
    })
}

models.EziliSupportModel = (b, t) => {
    t.buffs.push({
        name: translateValue('eziliZombieBuff'),
        pierceMultiplier: Display.multiplierPercent(b.necroPierceMultiplier),
        mutationId: Display.mutationId(b.NecroPierceMutator.id)
    })
}

models.FlagshipAttackSpeedIncreaseModel = (b, t) => {
    t.buffs.push({
        name: translateValue('flagshipBuff', 'flagship buff'),
        cooldownMultiplier: Display.multiplierPercent(b.attackSpeedIncrease),
        notes: [
            b.onlyAffectParagon ? translateValue('affectsOnlyParagon', 'only affects paragon') : null,
        ]
    })
}

models.FreeUpgradeSupportModel = (b, t) => {
    t.buffs.push({
        name: translateValue('freeUpgradeZone', 'free upgrade'),
        mutationId: Display.mutationId(b.mutatorId),
        maxTier: b.upgrade,
        filters: translateFilters(parseFilters(b.filters)),
        radius: b.isCustomRadius && b.customRadius < diagonalLength ? b.customRadius : null,
        maxStacks: b.maxStackSize > 0 ? b.maxStackSize : null,
        notes: [
            b.appliesToOwningTower ? translateValue('affectsSelf', 'affects self') : null,
            b.isGlobal || b.customRadius >= diagonalLength ? translateValue('affectGlobal', 'has global range') : null,
            b.onlyAffectParagon ? translateValue('affectsOnlyParagon', 'only affects paragon') : null,
        ]
    })
}

models.FreezeDurationSupportModel = (b, t) => {
    t.buffs.push({
        name: translateValue('freezeDurationBuff', 'freeze duration buff'),
        mutationId: Display.mutationId(b.mutatorId),
        lifespanMultiplier: Display.multiplierPercent(b.multiplier),
        notes: [
            b.appliesToOwningTower ? translateValue('affectsSelf', 'affects self') : null,
            b.isGlobal || b.customRadius >= diagonalLength ? translateValue('affectGlobal', 'has global range') : null,
            b.onlyAffectParagon ? translateValue('affectsOnlyParagon', 'only affects paragon') : null,
        ]
    })
}

models.FreezeNearbyWaterModel = (b, t) => {
    if (!b.radius) return
    t.zones.push({
        name: translateValue('freezeWaterZone', 'freeze nearby water'),
        radius: b.radius
    })
}

models.MagusPerfectusGraveyardStateManagerModel = (b, t) => {
    t.buffs.push({
        name: translateValue('manaGraveyardRateBuff', 'mana graveyard rate buff'),
        mutationId: Display.mutationId(b.RateMutator.id),
        multiplier: Display.multiplierPercent(b.rateMultplierAmount),
        rateBuffManaThreshold: Display.multiplierPercent(b.rateBonusThreshold)
    })
}

models.MoabShoveZoneModel = (b, t) => {
    t.zones.push({
        name: translateValue('shoveZone', 'shove'),
        speedMultiplierMoab: Display.multiplier(b.moabPushSpeedScaleCap),
        speedMultiplierBfb: Display.multiplier(b.bfbPushSpeedScaleCap),
        'speedMultiplierZomg/Ddt': Display.multiplier(b.zomgPushSpeedScaleCap),
        filters: translateFilters(parseFilters(b.filterInvisibleModel))
    })
}

models.MonkeyCityIncomeSupportModel = (b, t) => {
    t.buffs.push({
        name: translateValue('incomeBuffZone', 'income generation buff'),
        mutationId: Display.mutationId(b.uniqueMutatorId),
        incomeMultiplier: Display.additivePercent(b.incomeModifier - 1),
        filters: translateFilters(parseFilters(b.filters)),
        radius: b.isCustomRadius && b.customRadius < diagonalLength ? b.customRadius : null,
        maxStacks: b.maxStackSize > 0 ? b.maxStackSize : null,
    })
}

models.ObynGlobalSupportModel = (b, t) => {
    if (b.dotjRangeMultiplier > 0) {
        t.buffs.push({
            name: translateValue('obynBuffMiddleRange', 'obyn dotj buff'),
            mutationId: Display.mutationId(b.mutatorIdRange),
            rangeMultiplier: Display.additivePercent(b.dotjRangeMultiplier)
        })
    }
    if (b.dotsProjectileRadius > 0) {
        t.buffs.push({
            name: translateValue('obynBuffTopProjectileRadius', 'obyn dots proj radius buff'),
            mutationId: Display.mutationId(b.mutatorIdProjectileRadius),
            projectileRadiusMultiplier: Display.multiplierPercent(b.dotsProjectileRadius)
        })
    }
    if (b.tornadoAttackCooldownReduction > 0) {
        t.buffs.push({
            name: translateValue('obynBuffTopRate', 'obyn dots rate buff'),
            mutationId: Display.mutationId(b.mutatorIdTornadoRateMultiplier),
            cooldownMultiplier: Display.multiplierPercent(b.tornadoAttackCooldownReduction)
        })
    }
    if (b.mmAbilityCooldownMultiplier > 0) {
        t.buffs.push({
            name: translateValue('obynBuffMagicAbilityCooldown', 'obyn ability cooldown buff'),
            mutationId: Display.mutationId(b.mutatorIdCooldownMultiplier),
            cooldownScaleReduction: Display.additivePercent(-b.mmAbilityCooldownMultiplier)
        })
    }
}

models.PiercePercentageSupportModel = (b, t) => {
    t.buffs.push({
        name: translateValue('pierceBuffZone', 'pierce percent buff'),
        mutationId: Display.mutationId(b.mutatorId),
        piercePercent: Display.multiplierPercent(b.percentIncrease)
    })
}

models.PierceSupportModel = (b, t) => {
    t.buffs.push({
        name: translateValue('pierceBuffZone', 'pierce buff'),
        mutationId: Display.mutationId(b.mutatorId),
        additionalPierce: b.pierce ? Display.additive(b.pierce) : null,
        filters: translateFilters(parseFilters(b.filters)),
        radius: b.isCustomRadius && b.customRadius < diagonalLength ? b.customRadius : null,
        maxStacks: b.maxStackSize > 0 ? b.maxStackSize : null,
        notes: [
            b.appliesToOwningTower ? translateValue('affectsSelf', 'affects self') : null,
            b.isGlobal || b.customRadius >= diagonalLength ? translateValue('affectGlobal', 'has global range') : null,
            b.onlyAffectParagon ? translateValue('affectsOnlyParagon', 'only affects paragon') : null,
        ]
    })
}

models.PlacementAreaTypeRangeBuffModel = (b, t) => {
    t.buffs.push({
        name: translateValue('rangeBuffOnWater', 'range buff on water'),
        mutationId: Display.mutationId(b.Mutator.id),
        rangeMultiplier: Display.multiplierPercent(b.rangeMultiplier),
    })
}

models.PoplustSupportModel = (b, t) => {
    t.buffs.push({
        name: translateValue('poplustBuff', 'poplust'),
        mutationId: Display.mutationId(b.mutatorId),
        attackSpeedIncrease: Display.additivePercent(b.ratePercentIncrease),
        piercePercentIncrease: Display.additivePercent(b.piercePercentIncrease),
        maxStacks: b.maxStacks,
    })
}

models.PrinceOfDarknessZombieBuffModel = (b, t) => {
    t.buffs.push({
        name: translateValue('necromancerBuff', 'prince of darkness buff'),
        additionalDamage: Display.additive(b.damageIncrease),
        lifespanMultiplier: Display.multiplier(b.distanceMultiplier)
    })
}

models.ProjectileRadiusSupportModel = (b, t) => {
    t.buffs.push({
        name: translateValue('projectileRadiusBuffZone', 'projectile radius buff'),
        mutationId: Display.mutationId(b.mutatorId),
        projectileRadiusMultiplier: b.multiplier === 1 ? null : Display.multiplierPercent(b.multiplier),
        filters: translateFilters(parseFilters(b.filters)),
        radius: b.isCustomRadius && b.customRadius < diagonalLength ? b.customRadius : null,
        maxStacks: b.maxStackSize > 0 ? b.maxStackSize : null,
        notes: [
            b.appliesToOwningTower ? translateValue('affectsSelf', 'affects self') : null,
            b.isGlobal || b.customRadius >= diagonalLength ? translateValue('affectGlobal', 'has global range') : null,
            b.onlyAffectParagon ? translateValue('affectsOnlyParagon', 'only affects paragon') : null,
        ]
    })
}

models.ProjectileSpeedSupportModel = (b, t) => {
    t.buffs.push({
        name: translateValue('projectileSpeedZone', 'projectile speed buff'),
        mutationId: Display.mutationId(b.mutatorId),
        speedMultiplier: b.multiplier === 1 ? null : Display.additivePercent(b.multiplier),
        filters: translateFilters(parseFilters(b.filters)),
        radius: b.isCustomRadius && b.customRadius < diagonalLength ? b.customRadius : null,
        maxStacks: b.maxStackSize > 0 ? b.maxStackSize : null,
        notes: [
            b.appliesToOwningTower ? translateValue('affectsSelf', 'affects self') : null,
            b.isGlobal || b.customRadius >= diagonalLength ? translateValue('affectGlobal', 'has global range') : null,
            b.onlyAffectParagon ? translateValue('affectsOnlyParagon', 'only affects paragon') : null,
        ]
    })
}

models.PyrotechnicsSupportModel = (b, t) => {
    t.buffs.push({
        name: translateValue('gwenFireTowerBuff', 'gwen fire tower buff'),
        mutationId: Display.mutationId(b.mutatorId),
        cooldownMultiplier: Display.multiplierPercent(1 - b.percentIncrease),
        projectileRadiusMultiplier: Display.additivePercent(b.percentIncrease),
        notes: [
            b.appliesToOwningTower ? translateValue('affectsSelf', 'affects self') : null,
            b.isGlobal || b.customRadius >= diagonalLength ? translateValue('affectGlobal', 'has global range') : null,
            b.onlyAffectParagon ? translateValue('affectsOnlyParagon', 'only affects paragon') : null,
        ]
    })
}

models.RangeSupportModel = (b, t) => {
    t.buffs.push({
        name: translateValue('rangeBuffZone', 'range buff'),
        mutationId: Display.mutationId(b.mutatorId),
        rangeMultiplier: b.multiplier === 1 ? null : Display.additivePercent(b.multiplier),
        additionalRange: b.additive ? b.additive : null,
        filters: translateFilters(parseFilters(b.filters)),
        radius: b.isCustomRadius && b.customRadius < diagonalLength ? b.customRadius : null,
        maxStacks: b.maxStackSize > 0 ? b.maxStackSize : null,
        notes: [
            b.appliesToOwningTower ? translateValue('affectsSelf', 'affects self') : null,
            b.isGlobal || b.customRadius >= diagonalLength ? translateValue('affectGlobal', 'has global range') : null,
            b.onlyAffectParagon ? translateValue('affectsOnlyParagon', 'only affects paragon') : null,
        ]
    })
}

models.RateSupportBombExpertModel = (b, t) => {
    t.buffs.push({
        name: translateValue('jones8Buff', 'jones lvl8 buff'),
        rangeMultiplier: Display.additivePercent(b.rangeMultiplier),
        pierceMultiplier: Display.additivePercent(b.pierceMultiplier)
    })
}

models.RateSupportModel = (b, t) => {
    t.buffs.push({
        name: translateValue('rateBuffZone', 'attack cooldown buff'),
        mutationId: Display.mutationId(b.mutatorId),
        cooldownMultiplier: Display.multiplierPercent(b.multiplier),
        filters: translateFilters(parseFilters(b.filters)),
        radius: b.isCustomRadius && b.customRadius < diagonalLength ? b.customRadius : null,
        maxStacks: b.maxStackSize > 0 ? b.maxStackSize : null,
        notes: [
            b.appliesToOwningTower ? translateValue('affectsSelf', 'affects self') : null,
            b.isGlobal || b.customRadius >= diagonalLength ? translateValue('affectGlobal', 'has global range') : null,
            b.onlyAffectParagon ? translateValue('affectsOnlyParagon', 'only affects paragon') : null,
        ]
    })
}
models.RateSupportExplosiveModel = models.RateSupportModel

models.RoundBasedAttackSpeedModel = (b, t) => {
    t.buffs.push({
        name: translateValue('rateBuffOnRoundEnd', 'attack speed bonus per round'),
        speedBonusPerRound: Display.additivePercent(b.bonusAttackSpeedPerRoundPercent),
        minAttackCooldown: Display.time(b.maxWeaponAttackSpeed)
    })
}

models.SlowBloonsZoneModel = (b, t) => {
    let thing = {
        name: translateValue('slowZone', 'slows nearby bloons'),
        mutationId: Display.mutationId(b.mutationId),
        speedMultiplier: Display.multiplier(b.speedScale),
        filters: parseFilters(b.filters),
        radius: b.bindRadiusToTowerRange ? null : b.zoneRadius,
    }
    if (b.bloonTag) {
        thing.filters.push(new Filter(
            `filter${b.inclusive ? 'In' : 'Out'}Tags`,
            `can ${(b.inclusive ? 'only' : 'not')} affect ${b.bloonTags.join('/')}`,
            b.bloonTags.map(translateBloon).join('/')
        ))
    }
    thing.filters = translateFilters(thing.filters)
    t.zones.push(thing)
}

models.SpikeParagonDamageZoneModel = (b, t) => {
    models.DamageOverTimeZoneModel(b.damageOverTimeZoneModel, t)
}

models.SpiritOfTheForestModel = (b, t) => {
    models.DamageOverTimeZoneModel(b.damageOverTimeZoneModelFar, t)
    models.DamageOverTimeZoneModel(b.damageOverTimeZoneModelMiddle, t)
    t.zones[t.zones.length - 1].range = b.middleRange
    models.DamageOverTimeZoneModel(b.damageOverTimeZoneModelClose, t)
    t.zones[t.zones.length - 1].range = b.closeRange
}

models.StartOfRoundRateBuffModel = (b, t) => {
    t.buffs.push({
        name: translateValue('rateBuffOnRoundStart', 'start of round speed buff'),
        mutationId: Display.mutationId(b.mutatorId),
        cooldownMultiplier: Display.multiplierPercent(b.modifier),
        lifespan: Display.time(b.duration)
    })
}

models.SubCommanderSupportModel = (b, t) => {
    t.buffs.push({
        name: translateValue('subCommanderBuff', 'sub commander buff'),
        additionalPierce: Display.additive(b.pierceIncrease),
        damageMultiplier: Display.multiplier(b.damageScale),
        filters: translateFilters(parseFilters(b.filters))
    })
}

models.SubmergeModel = (b, t) => {
    const buff = {
        name: translateValue('reactorBuff', 'reactor buff')
    }
    if (b.heroXpScale !== 1) {
        buff.heroXpMultiplier = Display.multiplier(b.heroXpScale)
    }
    if (b.abilityCooldownSpeedScale !== 1) {
        buff.cooldownScaleReductionWaterTowerInRange = Display.additivePercent(1 - b.abilityCooldownSpeedScale)
    }
    if (b.abilityCooldownSpeedScaleGlobal !== 1) {
        buff.cooldownScaleReductionGlobal = Display.additivePercent(1 - b.abilityCooldownSpeedScaleGlobal)
    }
    if (Object.keys(buff).length > 1) {
        t.buffs.push(buff)
    }
    if (b.monkeySubParagonSupportModel) {
        t.buffs.push({
            name: translateValue('reactorBuffParagon', 'sub paragon submerge'),
            subPierceMultiplier: Display.multiplier(1 + b.monkeySubParagonSupportModel.subBonusPierceMultiplier),
            subDamageMultiplier: Display.multiplier(1 + b.monkeySubParagonSupportModel.subBonusDamageMultiplier),
            heroDamageMultiplier: Display.multiplier(1 + b.monkeySubParagonSupportModel.heroBonusDamageMultiplier),
            heroPierceMultiplier: Display.multiplier(1 + b.monkeySubParagonSupportModel.heroBonusPierceMultiplier),
            heroAttackCooldownMultiplier: Display.multiplierPercent(b.monkeySubParagonSupportModel.heroRateMultiplier),
            heroXpMultiplier: Display.multiplier(b.monkeySubParagonSupportModel.heroXpMultiplier)
        })
    }
}

models.SupportShinobiTacticsModel = (b, t) => {
    t.buffs.push({
        name: translateValue('shinobiStaticsBuff', 'shinobi tactics'),
        mutationId: Display.mutationId(b.mutatorId),
        pierceMultiplier: Display.additivePercent(1 - b.multiplier),
        cooldownMultiplier: Display.multiplierPercent(b.multiplier),
        filters: translateFilters(parseFilters(b.filters)),
        maxStacks: b.maxStacks
    })
}

models.SupportStackingRangeModel = (b, t) => {
    t.zones.push({
        name: translateValue('rangeNetwork', 'range network'),
        mutationId: Display.mutationId(b.mutatorId),
        rangeMultiplier: Display.additivePercent(b.rangeMultiplier),
        filters: translateFilters(parseFilters(b.filters)),
        maxStacks: b.maxStacks
    })
}

models.TargetSupplierSupportModel = ignoreFunction

models.TradeEmpireBuffModel = (b, t) => {
    t.buffs.push({
        name: translateValue('tradeEmpireBuff', 'trade empire'),
        mutationId: Display.mutationId(b.mutatorId),
        cashBonusPerT3Merchant: Display.additive(b.cashPerRoundPerMechantship),
        cashBonusPerT4Merchant: Display.additive(b.cashPerRoundPerFavouredTrades),
        additionalDamage: Display.additive(b.damageBuff),
        additionalCeramicDamage: Display.additive(b.ceramicDamageBuff),
        additionalBlimpDamage: Display.additive(b.moabDamageBuff),
        maxMerchants: b.maxMerchantmanCapBonus
    })
}

models.VigilanteTowerBehaviorModel = (b, t) => {
    t.buffs.push({
        name: translateValue('nomadBuff', 'nomad'),
        mutationId: Display.mutationId(b.Mutator.id),
        cooldownMultiplier: Display.multiplierPercent(b.loseLifeAttackSpeedBuff),
        additionalRange: Display.additive(b.loseLifeRangeBuff),
        bloonCashMultiplier: Display.additivePercent(b.bloonLeakValueModifier - 1),
        lifespan: Display.time(b.loseLifeBuffDurationFrames / 60),
        cooldown: Display.time(b.loseLifeBuffCooldownFrames / 60)
    })
}

models.VisibilitySupportModel = (b, t) => {
    t.buffs.push({
        name: translateValue('camoBuffZone', 'camo visibility'),
        mutationId: Display.mutationId(b.mutatorId),
        filters: translateFilters(parseFilters(b.filters)),
        radius: b.isCustomRadius && b.customRadius < diagonalLength ? b.customRadius : null,
        maxStacks: b.maxStackSize > 0 ? b.maxStackSize : null,
        notes: [
            b.appliesToOwningTower ? translateValue('affectsSelf', 'affects self') : null,
            b.isGlobal || b.customRadius >= diagonalLength ? translateValue('affectGlobal', 'has global range') : null,
            b.onlyAffectParagon ? translateValue('affectsOnlyParagon', 'only affects paragon') : null,
        ]
    })
}

models.WindyZoneModel = (b, t) => {
    t.zones.push({
        name: translateValue('blowbackBloons', 'blow back bloons'),
        mutationId: Display.mutationId(b.WindMutator.id),
        minDistance: b.distanceMin,
        maxDistance: b.distanceMax,
    }, {
        name: translateValue('slow', 'slow'),
        mutationId: Display.mutationId(b.SlowMutator.id),
        speedMultiplier: Display.multiplier(b.SlowMutator.multiplier),
        lifespanForMoab: Display.time(b.stunDurationMoab),
        lifespanForBfb: Display.time(b.stunDurationBfb),
        lifespanForZomg: Display.time(b.stunDurationZomg),
    })
}

//----------------------------------------------------
// corvus thing
//----------------------------------------------------
models.SpearModel = (b, t) => {
    t.spells.push({
        name: translateCorvusSpells('Spear', 'corvusSpear'),
        attacks: parseAttack(b.attack),
        initialManaCost: b.initialManaCost,
        ongoingManaCost: b.ongoingManaCost,
        interval: Display.time(b.manaDrainInterval)
    })
}

models.AggressionModel = (b, t) => {
    t.spells.push({
        name: translateCorvusSpells('Aggression', 'corvusAggression'),
        projectileRadiusMultiplier: Display.multiplierPercent(b.sizeMultiplier),
        rehitIntervalMultiplier: Display.multiplierPercent(b.rehitCooldownMultiplier),
        initialManaCost: b.initialManaCost,
        ongoingManaCost: b.ongoingManaCost,
        interval: Display.time(b.manaDrainInterval)
    })
}

models.MalevolenceModel = (b, t) => {
    t.spells.push({
        name: translateCorvusSpells('Malevolence', 'corvusMalevolence'),
        attacks: parseAttack(b.attack),
        initialManaCost: b.initialManaCost,
        ongoingManaCost: b.ongoingManaCost,
        interval: Display.time(b.manaDrainInterval)
    })
}

models.StormModel = (b, t) => {
    t.spells.push({
        name: translateCorvusSpells('Storm', 'corvusStorm'),
        attacks: parseAttack(b.attack),
        initialManaCost: b.initialManaCost,
        ongoingManaCost: b.ongoingManaCost,
        interval: Display.time(b.manaDrainInterval)
    })
}

models.RepelModel = (b, t) => {
    t.spells.push({
        name: translateCorvusSpells('Repel', 'corvusRepel'),
        projectile: parseProjectile(b.projectile),
        initialManaCost: b.initialManaCost,
        lifespan: Display.time(b.duration),
        cooldown: Display.time(b.cooldown)
    })
}

models.EchoModel = (b, t) => {
    t.spells.push({
        name: translateCorvusSpells('Echo', 'corvusEcho'),
        initialManaCost: b.initialManaCost,
        lifespan: Display.time(b.duration),
        cooldown: Display.time(b.cooldown)
    })
}

models.HasteModel = (b, t) => {
    t.spells.push({
        name: translateCorvusSpells('Haste', 'corvusHaste'),
        orbitSpeedMultiplier: b.orbitSpeedMultiplier,
        turnSpeedMultiplier: b.turnSpeedMultiplier,
        initialManaCost: b.initialManaCost,
        lifespan: Display.time(b.duration),
        cooldown: Display.time(b.cooldown)
    })
}

models.TrampleModel = (b, t) => {
    t.spells.push({
        name: translateCorvusSpells('Trample', 'corvusTrample'),
        projectile: parseProjectile(b.projectileModel),
        initialManaCost: b.initialManaCost,
        cooldown: Display.time(b.cooldown)
    })
}

models.FrostboundModel = (b, t) => {
    t.spells.push({
        name: translateCorvusSpells('Frostbound', 'corvusFrostbound'),
        projectile: parseProjectile(b.projectile),
        initialManaCost: b.initialManaCost,
        lifespan: Display.time(b.duration),
        cooldown: Display.time(b.cooldown)
    })
}

models.EmberModel = (b, t) => {
    t.spells.push({
        name: translateCorvusSpells('Ember', 'corvusEmber'),
        attacks: parseAttack(b.attack),
        initialManaCost: b.initialManaCost,
        lifespan: Display.time(b.duration),
        cooldown: Display.time(b.cooldown)
    })
}

models.AncestralMightModel = (b, t) => {
    t.spells.push({
        name: translateCorvusSpells('AncestralMight', 'corvusAncestralMight'),
        attacks: parseAttack(b.attack),
        initialManaCost: b.initialManaCost,
        lifespan: Display.time(b.duration),
        cooldown: Display.time(b.cooldown)
    })
}

models.OverloadModel = (b, t) => {
    t.spells.push({
        name: translateCorvusSpells('Overload', 'corvusOverload'),
        projectile: parseProjectile(b.projectile),
        initialManaCost: b.initialManaCost,
        lifespan: Display.time(b.duration),
        cooldown: Display.time(b.cooldown)
    })
}

models.NourishmentModel = (b, t) => {
    t.spells.push({
        name: translateCorvusSpells('Nourishment', 'corvusNourishment'),
        initialManaCost: '∞',
        cooldown: Display.time(b.cooldown)
    })
}

models.SoulBarrierModel = (b, t) => {
    t.spells.push({
        name: translateCorvusSpells('SoulBarrier', 'corvusSoulBarrier'),
        maxLivesAbsorbed: b.maxLivesAbsorbed,
        initialManaCost: b.initialManaCost,
        lifespan: Display.time(b.duration),
        cooldown: Display.time(b.cooldown)
    })
}

models.VisionModel = (b, t) => {
    t.spells.push({
        name: translateCorvusSpells('Vision', 'corvusVision'),
        initialManaCost: b.initialManaCost,
        lifespan: Display.time(b.duration),
        cooldown: Display.time(b.cooldown)
    })
}

models.RecoveryModel = (b, t) => {
    t.spells.push({
        name: translateCorvusSpells('Recovery', 'corvusRecovery'),
        initialManaCost: b.initialManaCost,
        lifespan: Display.time(b.duration),
        cooldown: Display.time(b.cooldown)
    })
}

models.SpiritualWillModel = (b, t) => {
    t.meta.manaPerSecond = b.manaPerSecond
}

function parseTowerBehavior(b, t, type = null) {
    if (!type) type = getModelType(b)
    if (models[type]) models[type](b, t)
    else t.behaviors.push(b)
}

module.exports = {
    parseTowerBehavior,
}